using System;
using System.IO;
using ParserGenerator.CodeGeneration;
using ParserGenerator.Exceptions;
using ParserGenerator.Extensions;
using ParserGenerator.GrammarConstruction;
using ParserGenerator.Output;
using ParserGenerator.ParserConstruction;
using ParserGenerator.ScannerConstruction;
using ParserGenerator.SyntacticAnalysis;

namespace ParserGenerator
{
    internal static class Program
    {
        private static void Main(string[] args)
        {
            //TestCSharpCodeGeneration();
            //TestGrammarParser();
            //Languages.Let.Program.Test();
            //RunParserGenerator(args);
            //ProgramDescriptionParser.Test();

            //CSharpProgramDescriptionWriter.Test();
            //CSharpProgramDescriptionWriter.TestWriter();

            //Languages.C.CProgramParser.Test();
            //Languages.CSharp.CSharpProgramParser.Test();

            //Languages.MSIL.MSILProgramParser.Test();
            //Languages.MSIL.MSILProgramParser.TestTokenizer();

            //Languages.LocalTypeInference.LocalTypeInferenceProgramParser.Test();

            TestCSharpTranslator();

            //Languages.JSON.JSONParser.Test();
        }

        private static void TestCSharpTranslator()
        {
            ExceptionExtensions.Interact(reader =>
            {
                ProgramDescriptionParser parser = new ProgramDescriptionParser(reader);
                ProgramDescriptionTranslator translator = new ProgramDescriptionTranslator();

                //Expression expression = parser.ParseSingleExpression().AsExpression.Value;
                //var translatedExpression = translator.TranslateExpression(expression);
                //Console.WriteLine(translatedExpression);

                var statements = parser.ParseInteractiveSegment().AsSegment.Body;
                foreach (Statement statement in statements)
                {
                    var translatedStatement = translator.TranslateStatement(statement);
                    Console.WriteLine(translatedStatement);
                }
            });
        }

        private static void TestCSharpCodeGeneration()
        {
            //ExceptionExtensions.Interact(reader =>
            //{
            //    ProgramDescriptionParser parser = new ProgramDescriptionParser(reader);
            //    ProgramDescription program = parser.ParseInteractiveSegment();
            //    ProgramDescriptionWriter writer = CSharpProgramDescriptionWriterFactory.Instance.CreateInstance();
            //    writer.WriteProgramDescription(program, new IndentedWriter());
            //});
        }

        private static void TestGrammarParser()
        {
            ExceptionExtensions.Protect(() =>
            {
                //string name = "../../../docs/Grammar.g";
                //string name = "../../../docs/Grammar2.g";
                //string name = "../../../docs/Grammar3.g";
                //string name = "../../../docs/Grammar4.g";
                //string name = "../../../docs/Grammar5.g";
                //string name = "../../../docs/Grammar6.g";
                //string name = "../../../docs/Grammar7.g";
                string name = "../../../docs/Grammar8.g";
                //string name = "Grammar4.g";
                string[] args = Environment.GetCommandLineArgs();
                if (args.Length > 1)
                    name = args[1];
#if FOO
                GrammarDescription grammar;
                using (StreamReader stream = new StreamReader(name))
                    grammar = GrammarDescription.FromStream(name, stream);
                grammar.Dump();
                ParserAutomatonTable parserTable = new ParserAutomatonTable(grammar.ParserAutomaton);
                //CSharpParserGenerator parserGenerator = new CSharpParserGenerator(parserTable);
                //parserGenerator.Generate();
                CSharpTypeDeclarationGenerator typeGenerator = new CSharpTypeDeclarationGenerator();
                typeGenerator.ProcessGrammarDescription(grammar, new IndentedWriter());
#endif
            });
        }

        private static void RunParserGenerator(string[] args)
        {
            if (args.Length == 0)
            {
                ProgramDescriptionParser.Test();
                return;
            }
            try
            {
                ProcessPaths(args);
            }
#if DEBUG
            catch (CompilationException e)
#else
                    catch (Exception e)
#endif
            {
                e.Print();
            }
        }

        private static void ProcessPaths(string[] paths)
        {
            if (paths == null)
                throw new ArgumentNullException(nameof(paths));
            foreach (string path in paths)
            {
                if (File.Exists(path))
                    ProcessFilePath(path);
                else if (Directory.Exists(path))
                    foreach (string inputFilePath in Directory.EnumerateFiles(path, "*.pg", SearchOption.AllDirectories))
                        ProcessFilePath(inputFilePath);
                else
                {
                    IOException e = new IOException(SR.PathNotFileOrDir);
                    throw e;
                }
            }
        }

        private static void ProcessFilePath(string inputFilePath)
        {
            if (inputFilePath == null)
                throw new ArgumentNullException(nameof(inputFilePath));
            GrammarDescription grammar = GrammarDescription.FromFile(inputFilePath);
            string baseDir = Path.GetDirectoryName(inputFilePath);
            string baseFileName = Path.GetFileNameWithoutExtension(inputFilePath);
            string loggingPath = Path.Combine(baseDir, baseFileName + ".g.log");
            grammar.DumpToDirectory(loggingPath);
            string outputFilePath = Path.Combine(baseDir, baseFileName + ".g.cs");
            if (File.Exists(outputFilePath))
                File.Delete(outputFilePath);
            using (StreamWriter stream = new StreamWriter(outputFilePath))
            {
                IndentedWriter writer = new IndentedWriter(stream);
                CSharpSemanticActionWriter tygen = new CSharpSemanticActionWriter(grammar.Declaration);
                bool flag = tygen.WriteTypeDefinitions(grammar.Declaration, writer);
                if (!grammar.ParserAutomaton.IsEmpty)
                {
                    if (flag)
                        writer.WriteLine();
                    ParserAutomatonTable tab = new ParserAutomatonTable(grammar.ParserAutomaton);
                    CSharpParserGenerator gen = new CSharpParserGenerator(grammar.Declaration, tab);
                    gen.Generate(writer);
                }
                else if (!grammar.ScannerAutomaton.IsEmpty)
                {
                    if (flag)
                        writer.WriteLine();
                    ScannerAutomatonTable tab = new ScannerAutomatonTable(grammar.ScannerAutomaton);
                    CSharpScannerGenerator gen = new CSharpScannerGenerator(grammar.Declaration, tab);
                    gen.Generate(writer);
                }
            }
        }
    }
}
